/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package clases;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Joaquin
 */
public class Recepcionista {

    /**
     * Instancia unica del Recepcionista
     */
    public static Recepcionista recepcionista = null;
    /**
     * Vector de vehiculos, los boxes, con un maximo de 5 espacios
     */
    private ArrayList<Box> boxes;

    private Recepcionista() {
        this.boxes = new ArrayList();
    }

    /**
     * Obtenemos la instancia unica del recepcionista, a través de un Singleton
     * @return el Recepcionista
     */
    public static Recepcionista getRecepcionista() {
        return Recepcionista.recepcionista==null?new Recepcionista():Recepcionista.recepcionista;
    }

    /**
     * Recibimos un vehiculo de un cliente y lo agregamos a boxes si hay lugar,
     * de lo contrario retornamos false
     * @param vehiculo
     */
    public synchronized void recibirVehiculo(Vehiculo vehiculo) {

        while (this.boxes.size() >= 5) {
            System.out.print("Recepcionista: estoy recibiendo una " +
                    vehiculo.toString() + " pero hay " + this.boxes.size() +
                    " boxes ocupados... vamos a esperar que de desocupen... \n");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Recepcionista.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // agregamos el vehiculo a un box
        this.boxes.add(new Box(vehiculo));
        this.notifyAll();

        System.out.print("Recepcionista: Agregue " + vehiculo.toString() + " a boxes \n");
    }

    /**
     * Recibimos un vehiculo de un cliente y lo agregamos a boxes si hay lugar,
     * de lo contrario retornamos false
     * @param vehiculo
     */
    public synchronized boolean recibirVehiculoReparado(Vehiculo vehiculo) {

        // mientras no haya espacios reservados esperamos
        while (!this.hayEspacioReservado()) {
            System.out.print("Recepcionista: No hay espacios reservados disponibles! \n");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Recepcionista.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        // de lo contrario colocamos en el primer box reservado el vehiculo reparado
        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            Box box = iterador.next();

            if (box.isReservado()) {
                System.out.print("Recepcionista: Acá hay un box RESERVADO para " + vehiculo.toString() + "\n");

                box.setVehiculo(vehiculo);
                this.notifyAll();
                return true;
            }
        }
        // si llegamos acá hay algun error
        System.out.print("Recepcionista: Acá NO hay ningún box RESERVADO (??) para " + vehiculo.toString());
        return false;
    }

    /**
     * Intentamos obtener un Vehiculo para ser reparado por parte de un Cliente,
     * que busca por codigo a su vehiculo
     * @return
     */
    public synchronized Vehiculo obtenerVehiculoReparado(int codigo) {
        // mientras no haya vehiculos reparados esperamos
        while (!this.estaReparado(codigo)) {
            System.out.println("Recepcionista: No hay vehiculos reparados todavía, pero estamos buscando el " + codigo + "\n");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Recepcionista.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // de lo contrario devolvemos el primer vehiculo reparado y notificamos
        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            Vehiculo vehiculo = iterador.next().getVehiculo();

            if (vehiculo.isReparado() && vehiculo.getCodigo() == codigo) {
                this.notifyAll();
                iterador.remove();
                
                System.out.print("Recepcionista: Hemos encontrado y obtenido el codigo: " +
                        codigo + ", vehiculo: " + vehiculo.toString() + " por lo que " +
                        "cobraremos: $" + calcularPrecioReparacion(vehiculo) + ".- \n");

                return vehiculo;
            }
        }

        // si no encontramos el vehiculo por el codigo salimos
        return null;
    }

    /**
     * Intentamos obtener un Vehiculo para ser reparado por parte de un Mecanico
     * @return
     */
    public synchronized Vehiculo obtenerVehiculoAReparar() {
        // mientras no haya vehiculos a reparar
        while (!this.hayVehiculoNoReparado()) {
            System.out.print("Recepcionista: Me pidieron algun vehiculo no reparado, pero no hay... \n");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Recepcionista.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        // de lo contrario obtenemos un vehiculo para reparar y notificamos
        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            Box box = iterador.next();

            if (!box.getVehiculo().isReparado()) {
                box.setReservado(true);
                this.notifyAll();

                System.out.print("Recepcionista: Vamos a reparar " + box.getVehiculo().toString() + "\n");

                return box.getVehiculo();
            }
        }
        // si llegamos aca hay algun problema
        return null;
    }

    private boolean estaReparado(int codigo) {
        if (this.hayVehiculoReparado()) {
            // de lo contrario devolvemos el primer vehiculo reparado y notificamos
            Iterator<Box> iterador = this.boxes.iterator();
            while (iterador.hasNext()) {
                Vehiculo vehiculo = iterador.next().getVehiculo();

                if (vehiculo.isReparado() && vehiculo.getCodigo() == codigo) {
                    this.notifyAll();
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Retornamos si hay espacio disponible para colocar un vehiculo
     * @return
     */
    private boolean hayEspacioReservado() {

        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            if (iterador.next().isReservado())
                return true;
        }

        return false;
    }

    private boolean hayVehiculoReparado() {
        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            if (iterador.next().getVehiculo().isReparado())
                return true;
        }

        return false;
    }

    public boolean hayVehiculoNoReparado() {
        Iterator<Box> iterador = this.boxes.iterator();
        while (iterador.hasNext()) {
            if (!iterador.next().getVehiculo().isReparado())
                return true;
        }

        return false;
    }

    /**
     * Calculamos el precio de reparacion
     * El monto a cobrar depende del vehículo que haya sido
     * reparado. En caso de ser una bicicleta se cobra $2 por segundo que demora
     * en repararla, y si es una moto cobra $3 por segundo invertido en su
     * reparación. En total se disponen de 5 boxes (los boxes deberán ser
     * simulados mediante la utilización de alguna colección a elección). La
     * ganancia del recepcionista es de $0.25 por cada vez que entrega y recibe
     * un vehículo. La ganancia del mecánico es el 15% del monto cobrado por
     * cada reparación.
     * @param vehiculo
     * @return El precio a cobrar por el total del servicio
     */
    private double calcularPrecioReparacion(Vehiculo vehiculo) {
        double retorno = 0;

        // si es moto o bicicleta
        retorno += vehiculo.getTiempoReparacion()/1000*(vehiculo instanceof Bicicleta?2:3);

        // sumamos 0.25 por comision del recepcionista
        retorno += 0.25;

        // sumamos 15% por comision del Mecanico
        retorno += (retorno/100*15);

        // retornamos
        return retorno;
    }
}
